// progtest.cc 
//	Test routines for demonstrating that Nachos can load
//	a user program and execute it.  
//
//	Also, routines for testing the Console hardware device.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "console.h"
#include "addrspace.h"
#include "synch.h"
#include "string.h"

//#ifdef CHANGED
#include "processtable.h"
#include "process.h"
//#endif //CHANGED

#define QUANTUM 100

//----------------------------------------------------------------------
// StartProcess
// 	Run a user program.  Open the executable, load it into
//	memory, and jump to it.
//----------------------------------------------------------------------
void
StartProcess(char *filename)
{
    OpenFile *executable = fileSystem->Open(filename);
    AddrSpace *space;
	Process* mainProcess;
	char* name;

    if (executable == NULL) {
		printf("[ERROR] Unable to open file %s\n", filename);
		return;
    }

	// allocate memory space for the main user program
    space = new AddrSpace(executable);

	// if unable to create AddrSpace at all, WHICH IS TOTALLY WEIRD
	if (space == NULL) {
		printf("[ERROR] Unable to create address space for file %s\n", filename);
		return;
	}

	// if unable to load the user program due to wrong file or something, stop
	if (space->validProgram == FALSE) {
		printf("[ERROR] Unable to load program onto address space for file %s\n", filename);
		delete space;
		return;
	}

	// if unable to allocate enough memory space (program + stack), then return, otherwise continue
	if (space->validPageTable == FALSE) {
		printf("Unable to allocate enough memory space for file %s\n", filename);
		delete space;
		return;
	}

	// create a process entry from the process table and associate the current thread (ie, main user thread with it)
	name = new char[20];
	sprintf(name, "process main");
	//processTableLock->Acquire();
	mainProcess = processTable->createProcess(name, space);
    currentThread->space = space;
	mainProcess->addThread(currentThread);
	currentThread->stackPointer = currentThread->space->getStackPointer(currentThread->threadID);
	//DEBUG('z', "Process [%s] with Main Thread [%s] and Thread Stack Pointer [%d] or [%x]\n", mainProcess->getName(), currentThread->getName(), currentThread->stackPointer, currentThread->stackPointer);
	//processTableLock->Release();

	//printf("Process [%d]: [%s] is created with Thread [%d]: [%s]\n", mainProcess->getProcessID(), mainProcess->getName(), currentThread->threadID, currentThread->getName());
 //   delete executable;			// close file

    space->InitRegisters();		// set the initial register values
    space->RestoreState();		// load page table register

    machine->Run();			// jump to the user progam
    ASSERT(FALSE);			// machine->Run never returns;
					// the address space exits
					// by doing the syscall "exit"
}

// Data structures needed for the console test.  Threads making
// I/O requests wait on a Semaphore to delay until the I/O completes.

static Console *console;
static Semaphore *readAvail;
static Semaphore *writeDone;

//----------------------------------------------------------------------
// ConsoleInterruptHandlers
// 	Wake up the thread that requested the I/O.
//----------------------------------------------------------------------

static void ReadAvail(int arg) { readAvail->V(); }
static void WriteDone(int arg) { writeDone->V(); }

//----------------------------------------------------------------------
// ConsoleTest
// 	Test the console by echoing characters typed at the input onto
//	the output.  Stop when the user types a 'q'.
//----------------------------------------------------------------------

void 
ConsoleTest (char *in, char *out)
{
    char ch;

    console = new Console(in, out, ReadAvail, WriteDone, 0);
    readAvail = new Semaphore("read avail", 0);
    writeDone = new Semaphore("write done", 0);
    
    for (;;) {
	readAvail->P();		// wait for character to arrive
	ch = console->GetChar();
	console->PutChar(ch);	// echo it!
	writeDone->P() ;        // wait for write to finish
	if (ch == 'q') return;  // if q, quit
    }
}

